﻿using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Ewk.Runtime.Serialization.PositionBased;
using Ewk.Runtime.Serialization.UnitTests.Records.PositionBased;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Reflection;
using Ewk.Runtime.Serialization;

namespace Ewk.Runtime.Serialization.UnitTests.PositionBased
{
    /// <summary>
    /// Summary description for PositionBasedDatafieldAttribute_Int32Tests
    /// </summary>
    [TestClass]
    public class PositionBasedDatafieldAttribute_Int32Tests
    {
        #region Additional test attributes
        //
        // You can use the following additional attributes as you write your tests:
        //
        // Use ClassInitialize to run code before running the first test in the class
        // [ClassInitialize()]
        // public static void MyClassInitialize(TestContext testContext) { }
        //
        // Use ClassCleanup to run code after all tests in a class have run
        // [ClassCleanup()]
        // public static void MyClassCleanup() { }
        //
        // Use TestInitialize to run code before running each test 
        // [TestInitialize()]
        // public void MyTestInitialize() { }
        //
        // Use TestCleanup to run code after each test has run
        // [TestCleanup()]
        // public void MyTestCleanup() { }
        //
        #endregion

        [TestMethod, ExpectedException(typeof(DatafieldEmptyException))]
        public void PositionBasedDatafieldAttribute_cannot_deserialize_a_mandatory_empty_Int32_string_with_to_an_Int32_instance()
        {
            var fieldAttribute = new PositionBasedDatafieldAttribute(0, 1) {Mandatory = true, DefaultCharacter = ' '};
            Deserialize(fieldAttribute, " ");
        }

        [TestMethod]
        public void PositionBasedDatafieldAttribute_can_deserialize_a_mandatory_empty_Int32_string_with_to_an_Int32_instance_when_subscribed_to_the_DatafieldEmpty_event()
        {
            string eventMessage = null;

            var fieldAttribute = new PositionBasedDatafieldAttribute(0, 1) { Mandatory = true, DefaultCharacter = ' ' };
            fieldAttribute.DatafieldEmpty += delegate(object sender, DatafieldEmptyEventArgs args)
            {
                eventMessage = args.Message;
            };
            Deserialize(fieldAttribute, " ");

            Assert.IsInstanceOfType(eventMessage, typeof(string));
        }

        [TestMethod]
        public void PositionBasedDatafieldAttribute_can_deserialize_a_left_aligned_valid_negative_Int32_string_with_aberrant_DefaultCharacter_to_a_Int32_instance()
        {
            var fieldAttribute = new PositionBasedDatafieldAttribute(0, 10) { DefaultCharacter = 'X', Alignment = DatafieldAlignment.Left };

            for (var i = -99; i < -9; i++)
            {
                var result = Deserialize(fieldAttribute, i.ToString("00XXXXXXXX;-00XXXXXXX"));

                Assert.IsInstanceOfType(result, typeof(Int32));

                var typedResult = (Int32)result;
                Assert.AreEqual(i, typedResult);
            }
        }

        [TestMethod]
        public void PositionBasedDatafieldAttribute_can_deserialize_a_left_aligned_valid_positive_Int32_string_with_aberrant_DefaultCharacter_to_a_Int32_instance()
        {
            var fieldAttribute = new PositionBasedDatafieldAttribute(0, 10) { DefaultCharacter = 'X', Alignment = DatafieldAlignment.Left };

            for (var i = 10; i < 100; i++)
            {
                var result = Deserialize(fieldAttribute, i.ToString("00XXXXXXXX;-00XXXXXXX"));

                Assert.IsInstanceOfType(result, typeof(Int32));

                var typedResult = (Int32)result;
                Assert.AreEqual(i, typedResult);
            }
        }

        [TestMethod]
        public void PositionBasedDatafieldAttribute_can_deserialize_a_valid_negative_Int32_string_with_aberrant_DefaultCharacter_to_a_Int32_instance()
        {
            var fieldAttribute = new PositionBasedDatafieldAttribute(0, 10) { DefaultCharacter = 'X', Alignment = DatafieldAlignment.Left };

            for (var i = -99; i < -9; i++)
            {
                var result = Deserialize(fieldAttribute, i.ToString("00XXXXXXXX;-00XXXXXXX"));

                Assert.IsInstanceOfType(result, typeof(Int32));

                var typedResult = (Int32)result;
                Assert.AreEqual(i, typedResult);
            }
        }

        [TestMethod]
        public void PositionBasedDatafieldAttribute_can_deserialize_a_valid_positive_Int32_string_with_aberrant_DefaultCharacter_to_a_Int32_instance()
        {
            var fieldAttribute = new PositionBasedDatafieldAttribute(0, 10) { DefaultCharacter = 'X', Alignment = DatafieldAlignment.Left };

            for (var i = 10; i < 100; i++)
            {
                var result = Deserialize(fieldAttribute, i.ToString("00XXXXXXXX;-00XXXXXXX"));

                Assert.IsInstanceOfType(result, typeof(Int32));

                var typedResult = (Int32)result;
                Assert.AreEqual(i, typedResult);
            }
        }

        [TestMethod]
        public void PositionBasedDatafieldAttribute_can_deserialize_a_valid_zero_Int32_string_with_aberrant_DefaultCharacter_to_a_Int32_instance()
        {
            var fieldAttribute = new PositionBasedDatafieldAttribute(0, 10) { DefaultCharacter = 'X', Alignment = DatafieldAlignment.Left };
            var result = Deserialize(fieldAttribute, "XXXXXXXXXX");

            Assert.IsInstanceOfType(result, typeof(int));

            var typedResult = (int)result;
            Assert.AreEqual(default(int), typedResult);
        }

        [TestMethod]
        public void PositionBasedDatafieldAttribute_can_serialize_a_neqative_Int32_to_a_Int32_string_with_aberrant_DefaultCharacter()
        {
            var fieldAttribute = new PositionBasedDatafieldAttribute(0, 10) { DefaultCharacter = 'X' };

            for (var i = -99; i < -9; i++)
            {
                var datarecord = new PositionBasedTestDatarecordWithIntegerField { Field = i };
                var result = Serialize(fieldAttribute, datarecord);

                Assert.AreEqual(10, result.Length, "Incorrect length");
                Assert.AreEqual(i.ToString("XXXXXXXX00;XXXXXXX-00"), result, "Incorrect value");
            }
        }

        [TestMethod]
        public void PositionBasedDatafieldAttribute_can_serialize_a_neqative_Int32_to_a_left_aligned_Int32_string_with_aberrant_DefaultCharacter()
        {
            var fieldAttribute = new PositionBasedDatafieldAttribute(0, 10) { DefaultCharacter = 'X', Alignment = DatafieldAlignment.Left };

            for (var i = -99; i < -9; i++)
            {
                var datarecord = new PositionBasedTestDatarecordWithIntegerField { Field = i };
                var result = Serialize(fieldAttribute, datarecord);

                Assert.AreEqual(10, result.Length, "Incorrect length");
                Assert.AreEqual(i.ToString("00XXXXXXXX;-00XXXXXXX"), result, "Incorrect value");
            }
        }

        [TestMethod]
        public void PositionBasedDatafieldAttribute_can_serialize_a_positive_Int32_to_a_Int32_string_with_aberrant_DefaultCharacter()
        {
            var fieldAttribute = new PositionBasedDatafieldAttribute(0, 10) { DefaultCharacter = 'X' };

            for (var i = 10; i < 100; i++)
            {
                var datarecord = new PositionBasedTestDatarecordWithIntegerField { Field = i };
                var result = Serialize(fieldAttribute, datarecord);

                Assert.AreEqual(10, result.Length, "Incorrect length");
                Assert.AreEqual(i.ToString("XXXXXXXX00;XXXXXXX-00"), result, "Incorrect value");
            }
        }

        [TestMethod]
        public void PositionBasedDatafieldAttribute_can_serialize_a_positive_Int32_to_a_left_aligned_Int32_string_with_aberrant_DefaultCharacter()
        {
            var fieldAttribute = new PositionBasedDatafieldAttribute(0, 10) { DefaultCharacter = 'X', Alignment = DatafieldAlignment.Left };

            for (var i = 10; i < 100; i++)
            {
                var datarecord = new PositionBasedTestDatarecordWithIntegerField { Field = i };
                var result = Serialize(fieldAttribute, datarecord);

                Assert.AreEqual(10, result.Length, "Incorrect length");
                Assert.AreEqual(i.ToString("00XXXXXXXX;-00XXXXXXX"), result, "Incorrect value");
            }
        }

        [TestMethod, ExpectedException(typeof(DatafieldOverflowException))]
        public void PositionBasedDatafieldAttribute_cannot_serialize_a_positive_Int32_that_is_too_large_for_the_string_representation_of_the_Int32()
        {
            var fieldAttribute = new PositionBasedDatafieldAttribute(0, 1) { DefaultCharacter = '0' };
            var datarecord = new PositionBasedTestDatarecordWithIntegerField { Field = 11 };
            
            Serialize(fieldAttribute, datarecord);
        }

        [TestMethod]
        public void PositionBasedDatafieldAttribute_can_serialize_a_positive_Int32_that_is_too_large_for_the_string_representation_of_the_Int32_when_overflow_event_is_subscribed()
        {
            string eventMessage = null;
            var fieldAttribute = new PositionBasedDatafieldAttribute(0, 1) { DefaultCharacter = '0' };
            fieldAttribute.DatafieldOverflow += delegate(object sender, DatafieldOverflowEventArgs args)
                                                    {
                                                        eventMessage = args.Message;
                                                    };
            var datarecord = new PositionBasedTestDatarecordWithIntegerField { Field = 11 };

            Serialize(fieldAttribute, datarecord);

            Assert.IsInstanceOfType(eventMessage, typeof(string));
        }

        [TestMethod]
        public void PositionBasedDatafieldAttribute_cannot_serialize_a_negative_Int32_that_is_too_large_for_the_string_representation_of_the_Int32()
        {
            var fieldAttribute = new PositionBasedDatafieldAttribute(0, 1) { DefaultCharacter = '0' };
            var datarecord = new PositionBasedTestDatarecordWithIntegerField { Field = -1 };
            var result = Serialize(fieldAttribute, datarecord);

            Assert.AreNotEqual("-1", result, "Incorrect value");
        }

        string Serialize(DatafieldAttribute attribute, PositionBasedTestDatarecordWithIntegerField datarecord)
        {
            attribute.PropertyInfo = GetPropertyInfo(datarecord);
            return attribute.Serialize(datarecord);
        }

        object Deserialize(DatafieldAttribute attribute, string value)
        {
            attribute.PropertyInfo = GetPropertyInfo();
            return attribute.Deserialize(value);
        }

        static PropertyInfo GetPropertyInfo()
        {
            return typeof(PositionBasedTestDatarecordWithIntegerField).GetProperty("Field");
        }

        static PropertyInfo GetPropertyInfo(PositionBasedTestDatarecordWithIntegerField record)
        {
            return record.GetType().GetProperty("Field");
        }
    }
}
